#include "osdeamonservice.h"

#include <QEventLoop>
#include <QFileInfo>
#include <QJsonArray>
#include <QJsonDocument>
#include <QProcess>
#include <QSettings>
#include <QStack>
#include <QThreadPool>
#include <QtConcurrent>
#include <algorithm>
#include <QDateTime>
#include "ebnetwork.h"

#define PRODUCT_UNZIP_PATH      "/opt/ota/down/"
#define MAX_THREAD_COUNT        (10)
namespace Auxi{
bool increSort(const QString &a1,const QString &a2);
}
OSDeamonService::OSDeamonService(QObject *parent)
    : QObject{parent}
{
    qDebug()<<"> "<<__func__<<"thread id:"<<QThread::currentThreadId();
    m_thd = new QThread();
    this->moveToThread(m_thd);
    connect(m_thd,&QThread::started,this,&OSDeamonService::onInit,Qt::QueuedConnection);
    connect(m_thd,&QThread::finished,m_thd,&QThread::deleteLater,Qt::QueuedConnection);
    connect(m_thd,&QThread::destroyed,this,&OSDeamonService::deleteLater,Qt::QueuedConnection);
}

OSDeamonService::~OSDeamonService()
{
    qDebug()<<"> "<<__func__;
    if(m_daemonProxy)
        m_daemonProxy->onDisconnectAllClient();
}

void OSDeamonService::start(){
    if(m_thd)
        m_thd->start();
}

void OSDeamonService::onInit()
{
    qDebug()<<"> "<<__func__<<"thread id:"<<QThread::currentThreadId();
    qRegisterMetaType<QProcess::ExitStatus>("QProcess::ExitStatus");
    // 控制线程池大小
    QThreadPool::globalInstance()->setMaxThreadCount(MAX_THREAD_COUNT);
    m_configManage = new ConfigManage(this);
    //TODO 虽然是个服务，但是还是不要杀掉的好；在程序内进行重启..
    // if(!m_configManage->init()){
    //     qDebug()<<"> init error,kill Process!";
    //     exit(-1);
    // }
    while(!m_configManage->init()){
        qDebug()<<"> onInit() Initialization failed. Read again after 30 seconds...";
        QThread::sleep(30);
    }
    QString time = QString::number(QDateTime::currentMSecsSinceEpoch());
    QString clientId = QString("%1&%2").arg(m_configManage->deviceId(),time);
    
    QMap<QString,QString> mqttInfoMap;
    mqttInfoMap.insert("productKey",m_configManage->productKey());
    mqttInfoMap.insert("clientId",clientId);
    mqttInfoMap.insert("productSecret",m_configManage->productScrent());
    mqttInfoMap.insert("deviceName",m_configManage->deviceId());
    mqttInfoMap.insert("hostname",m_configManage->hostname());
    mqttInfoMap.insert("port",QString::number(m_configManage->portRegister()));
    
    m_mqttDevReg = new MqttDeviceReg(mqttInfoMap,this);
    m_daemonProxy = new DaemonProxy(this);
    m_downloadTasks = new DownloadTasks(this);
    //DownloadTasks
    connect(this,&OSDeamonService::sigStartDownloadTasks,m_downloadTasks,&DownloadTasks::startRequest,Qt::QueuedConnection);
    connect(m_downloadTasks,&DownloadTasks::sigAppearError,this,[&](){
            setDlExistError(true);
        },Qt::QueuedConnection);
    connect(m_downloadTasks,&DownloadTasks::sigDownloadSche,this,[&](QString rate){
            qDebug()<<"> init() rate:"<<rate;
            QStringList msgLis = {"upgradeProgress",QString::number(qreal(rate.toDouble()/2))};
            emit sigSocketSendMsg(DaemonProxy::ProcessObj::PO_Upgrade,msgLis.join("@&@"));
        },Qt::QueuedConnection);
    connect(m_downloadTasks,&DownloadTasks::postReply,this,&OSDeamonService::httpReply,Qt::QueuedConnection);
    connect(m_downloadTasks,&DownloadTasks::sigTimerout,this,[&](QNetworkReply::NetworkError error){
            if(error == QNetworkReply::NetworkError::OperationCanceledError){
                QStringList list = {"showErrorCode","1005"};
                emit sigSocketSendMsg(DaemonProxy::ProcessObj::PO_Upgrade,list.join("@&@"));
                setDlExistError(true);
            }
        },Qt::QueuedConnection);
    //DaemonProxy
    connect(m_daemonProxy,&DaemonProxy::sigPostOtaFirmwareGet,this,&OSDeamonService::onPostOtaFirmwareGet,Qt::QueuedConnection);
    connect(this,&OSDeamonService::sigSocketSendMsg,m_daemonProxy,&DaemonProxy::onSendMsg,Qt::QueuedConnection);
    connect(m_daemonProxy,&DaemonProxy::sigReadTaskFile,this,&OSDeamonService::onReadTaskFile,Qt::QueuedConnection);
    connect(m_daemonProxy,&DaemonProxy::sigDoUpgrade,this,&OSDeamonService::onDoUpgrade,Qt::QueuedConnection);
    connect(m_daemonProxy,&DaemonProxy::sigTipStatus,m_configManage,&ConfigManage::onTipstatus,Qt::QueuedConnection);
    connect(m_daemonProxy,&DaemonProxy::sigPower,this,&OSDeamonService::onPower,Qt::QueuedConnection);
    connect(m_daemonProxy,&DaemonProxy::sigSDDMRegister,this,&OSDeamonService::onSDDMRegister,Qt::QueuedConnection);
    connect(m_daemonProxy,&DaemonProxy::sigReadWallpaper,m_configManage,&ConfigManage::onReadWallpaper,Qt::QueuedConnection);
    //configmanage
    connect(m_configManage,&ConfigManage::sigUserWallpaperPath,this,[&](QString path){
            QStringList list = {"recvWallpaper",path};
            emit sigSocketSendMsg(DaemonProxy::ProcessObj::PO_Upgrade,list.join("@&@"));
        },Qt::QueuedConnection);
    //MqttDeviceReg
    connect(m_mqttDevReg,&MqttDeviceReg::register_reply,this,&OSDeamonService::onRegisterRespons,Qt::QueuedConnection);
    connect(EBNetwork::Get(),&EBNetwork::netStatusChanged,this,&OSDeamonService::onMqttRegister,Qt::QueuedConnection);
    
    //this
    connect(this,&OSDeamonService::sigUpdate,this,&OSDeamonService::onUpdateLogic,Qt::QueuedConnection);
    connect(this,&OSDeamonService::sigInstallPackage,this,&OSDeamonService::installUpdatePackage,Qt::QueuedConnection);
    connect(this,&OSDeamonService::sigGetNewVersion,this,[&](){
        QJsonObject jsObj;
        jsObj.insert("firmware_version",m_configManage->productVersion());
        m_mqttDaemon->publishTheme(MqttDaemon::Topic::OTA_pub_update_firmware,jsObj);
    },Qt::QueuedConnection);
    
    checkNetwork();
    if(m_daemonProxy->socketServer()){
        system("chmod 777 /tmp/os_daemon");
    }
    qDebug()<<"> mqtt connect data:"<<mqttInfoMap;
}

void OSDeamonService::onMqttRegister()
{
    if(!checkNetwork())
        return;
    m_mqttDevReg->mqttMonitor();  
}

void OSDeamonService::onRegisterRespons(QJsonObject jsObj)
{
    qDebug()<<"> "<<__func__<<jsObj;
    qDebug()<<"> "<<__func__<<" json object key code:"<<jsObj.value("code").toString();
    if(jsObj.isEmpty()){
        qDebug()<<"> "<<__func__<<" json package is null or m_mqttDaemon object exist!";
        return;
    }
    QMap<QString,QString> mqttInfoMap;
    if(jsObj.contains("msg") && jsObj.value("msg").toString()=="success"){
        //注册成功
        m_registerOk = true;

#ifdef ESPORT_BOX
        //游戏盒子绑定代理商
        QJsonObject jsSN;
        jsSN.insert("device_sn",m_configManage->deviceId());
        m_downloadTasks->postHttp(m_configManage->urlBindAgent(),jsSN);
#endif
        
        if(jsObj.contains("data") && !m_mqttDaemon){
            QJsonObject dataJsObj = jsObj.value("data").toObject();
            if(dataJsObj.contains("mqtt")){
                
                QJsonObject mqttJsObj = dataJsObj.value("mqtt").toObject();
                QString clientId,password,username;
                if(mqttJsObj.contains("client_id")){
                    clientId = mqttJsObj.value("client_id").toString();
                }
                if(mqttJsObj.contains("password")){
                    password = mqttJsObj.value("password").toString();
                }
                if(mqttJsObj.contains("username")){
                    username = mqttJsObj.value("username").toString();
                }
                mqttInfoMap.insert("clientId",clientId);
                mqttInfoMap.insert("password",password);
                mqttInfoMap.insert("username",username);
                
                mqttInfoMap.insert("productKey",m_configManage->productKey());
                mqttInfoMap.insert("productSecret",m_configManage->productScrent());
                mqttInfoMap.insert("deviceName",m_configManage->deviceId());
                mqttInfoMap.insert("hostname",m_configManage->hostname());
                mqttInfoMap.insert("port",QString::number(m_configManage->portDaemon()));
                
            }else{
                qDebug()<<"> "<<__func__<<"jsong package not \"mqtt\" key";
            }
        }else{
            if(!m_mqttDaemon)
                qDebug()<<"> "<<__func__<<"jsong package not \"data\" key";
        } 
    }else{
        //TODO sddm
        m_registerOk = false;
        qDebug()<<"> "<<__func__<<"jsong package not \"msg\" key or msg info not sucess:"+jsObj.value("msg").toString()
                 <<" error code:"<<jsObj.value("code").toString();
    }
    m_configManage->writeSDDMRegister(m_registerOk);
    emit sigSocketSendMsg(DaemonProxy::ProcessObj::PO_SDDM,m_registerOk?"success":"failed");
    initMqttDaemon(mqttInfoMap);
    sendMacIP();
}

void OSDeamonService::onRecvDeamonConn()
{
    if(!m_mqttDaemon){
        qDebug()<<"> "<<__func__<<__LINE__<<"m_mqttDaemon object is null";
        return;
    }
    m_connectMqttDaemon = true;
    //mqtt daemon 接口返回的值的信号槽
    connect(m_mqttDaemon,&MqttDaemon::downloadFirmware,this,&OSDeamonService::onRecvDownloadFirmware,
            (Qt::ConnectionType)(Qt::QueuedConnection | Qt::UniqueConnection));
    //subscription theme
    m_mqttDaemon->subscribeTheme(MqttDaemon::Topic::OTA_sub_download_firmware);
    m_mqttDaemon->subscribeTheme(MqttDaemon::Topic::Property_sub_cloud_response);
    m_mqttDaemon->subscribeTheme(MqttDaemon::Topic::ConfigUpdate_sub_Cloud_response);
    m_mqttDaemon->subscribeTheme(MqttDaemon::Topic::Property_sub_heartReply);
    //publish theme
    m_mqttDaemon->publishTheme(MqttDaemon::Topic::ConfigUpdate_pub_device_item_query,QJsonObject());
    
    if(m_requestFirmware){
        m_requestFirmware = false;
        onPostOtaFirmwareGet();
    }
}

void OSDeamonService::onRecvDownloadFirmware(QJsonObject jsobj)
{
    qDebug()<<"> onRecvDownloadFirmware() data:"<<jsobj;

    if(jsobj.isEmpty() || jsobj.value("code").toInt() != 0){
        qDebug()<<"> "<<__func__<<" error code:"<<jsobj.value("code");
        qDebug()<<"> "<<__func__<<" json package is null!";
        return;
    }
    m_tasksQueue.clear();
    qDebug()<<"> onRecvDownloadFirmware() first if()";
    if(m_updateFail){
        QJsonArray jsarray = jsobj.value("data").toArray();
        if(jsarray.size() == 0){
            m_updataVersionDate = true;
            m_daemonProxy->reboot()?onReboot():m_daemonProxy->shutdown()?onShutdown():void();
        }
    }
    qDebug()<<"> onRecvDownloadFirmware() second if()";
    if(jsobj.contains("msg") && jsobj.value("msg").toString()=="success"){
        QJsonArray jsarray = jsobj.value("data").toArray();
        for(int i = 0;i < jsarray.size(); i++){
            QString pversion = jsarray[i].toObject().value("version").toString();
            pversion = m_configManage->removeLeadingZeros(pversion);
            //当前版本小于更新包版本，就加入容器
            if(Auxi::increSort(m_configManage->productVersion(),pversion))
                m_tasksQueue.append(jsarray[i].toObject());
        }
        m_isPckDateOK = true;
    }else{
        qDebug()<<"> "<<__func__<<"jsong package not \"msg\" key or msg info not sucess:"+jsobj.value("msg").toString()
                 <<" error code:"<<jsobj.value("code").toString();
    }
    qDebug()<<"> onRecvDownloadFirmware() json:"<<m_tasksQueue;
    //emit sigUpdate();
    onUpdateLogic();
}

void OSDeamonService::onPostOtaFirmwareGet()
{
    qDebug()<<"> onPostOtaFirmwareGet() get update firmware!";
    m_configManage->readAutoUpdate();
    if(!m_mqttDaemon || !checkNetwork()){
        m_requestFirmware = true;
#ifdef ESPORT_BOX
        emit sigSocketSendMsg(DaemonProxy::ProcessObj::PO_EsportBox,"NotNetwork");
#else
        emit sigSocketSendMsg(DaemonProxy::ProcessObj::PO_PersonCenter,"NotNetwork");
#endif
        return;
    }
    if(m_connectMqttDaemon){
        //publish theme
        QJsonObject jsObj;
        jsObj.insert("firmware_version",m_configManage->productVersion());
        m_mqttDaemon->publishTheme(MqttDaemon::Topic::OTA_pub_update_firmware,jsObj);
        qDebug()<<"> OSDeamonService::onPostOtaFirmwareGet() get upgrade package info!";
    }else{
        qDebug()<<"> onPostOtaFirmwareGet() wait daemon mqtt reconnect...";
    }
    qDebug()<<"> onPostOtaFirmwareGet() end!";
}

void OSDeamonService::onUpdateLogic()
{
    qDebug()<<"- onUpdateLogic() start!";
    //确定是否有更新
    if(m_tasksQueue.size()<=0){
        qDebug()<<"> not exist update package!";
#ifdef ESPORT_BOX
        emit sigSocketSendMsg(DaemonProxy::ProcessObj::PO_EsportBox,"nothing");
#else
        emit sigSocketSendMsg(DaemonProxy::ProcessObj::PO_PersonCenter,"nothing");
#endif
        return;
    }

    //递增排序
    std::sort(m_tasksQueue.begin(),m_tasksQueue.end(),[](QJsonObject jsA,QJsonObject jsB){
        QString s1 = jsA.value("version").toString();
        QString s2 = jsB.value("version").toString();
        return Auxi::increSort(s1,s2);
    });
    
    //筛选哪些包是需要更新
    QList<QJsonObject> stack;
    for(int i = 0; i < m_tasksQueue.size(); i++){
        //0：完整，1：补丁
        int flag = m_tasksQueue[i].value("file_type").toInt();
        if(flag == 0){
            stack.clear();
            stack.append(m_tasksQueue[i]);
        }else{
            stack.append(m_tasksQueue[i]);
        }
    }
    
    m_tasksQueue.clear();
    m_tasksQueue = stack;
    
    if(m_tasksQueue.size()<=0)
        return;
    
    //存在一个"强升"标志则进行升级
    m_upgradeSilent = false;
    for(auto& v : m_tasksQueue){
        if(v.value("pattern").toInt() == 1){
            m_upgradeSilent = true;
            break;
        }
    }
    
    QString show_version = m_configManage->removeLeadingZeros(m_tasksQueue.last().value("version").toString());
    QString show_remark = m_tasksQueue.last().value("change_log").toString();
    //QStringList msgList = {"showUpgrade",show_version,show_remark};
    QStringList userChckList = {"showUpgrade",show_version,show_remark,m_popup[0]};
    QStringList autoCheckList = {"showUpgrade",show_version,show_remark,m_popup[1]};
    //如果是个人中心手动检测的
    if(m_daemonProxy->userCheck()){
        //给个人中心发消息，存在升级包
#ifdef ESPORT_BOX
        emit sigSocketSendMsg(DaemonProxy::ProcessObj::PO_EsportBox,"have");
#else
        emit sigSocketSendMsg(DaemonProxy::ProcessObj::PO_PersonCenter,"have");
#endif
        //则给upgrade发送消息,显示弹窗
        emit sigSocketSendMsg(DaemonProxy::ProcessObj::PO_Upgrade,userChckList.join("@&@"));
    }else if(!m_upgradeSilent && !m_configManage->autoUpgrade()){
        if(m_configManage->tipTimeStatus() == 2){
            // 超过一周，继续提醒
            QDateTime time = QDateTime::currentDateTime();
            int days = time.daysTo(m_configManage->tipTime());
            qDebug()<<"< onUpdateLogic() day:"<<days <<QString(" qAbs(%1)").arg(qAbs(days));
            if(qAbs(days) > 7){
                //则给upgrade发送消息,显示弹窗
                emit sigSocketSendMsg(DaemonProxy::ProcessObj::PO_Upgrade,autoCheckList.join("@&@"));
            }
        }else{
            //则给upgrade发送消息,显示弹窗
            emit sigSocketSendMsg(DaemonProxy::ProcessObj::PO_Upgrade,autoCheckList.join("@&@"));
        }
    }else{
        qDebug()<<"> onUpdateLogic() upgeade app invoking!";
        if(isUpdatePckExists(m_tasksQueue,m_fileSavePath)){
            // 强制升级
            m_configManage->wirteUpdateLoadingFlag(ConfigManage::UpdateFlag::UF_StartUpdate);
            m_configManage->writeUpdatePackageDate(m_tasksQueue);
        }else{
            //下载升级包
            m_queueSize = m_tasksQueue.size();
            emit sigStartDownloadTasks();
        }
    }
    return;
}

void OSDeamonService::onDownloadFinished(QObject *)
{
    static int count = 1;
    qDebug()<<"> "<<__func__<<"download sucessful";
    --m_queueSize;
    if(m_queueSize>0)
        return;
    //现在出现错误，需要重新再下载一次
    if(m_dlExistError && count < 2){
        ++count;
        m_dlExistError = false;
        m_queueSize = m_tasksQueue.size();
        emit sigStartDownloadTasks();
        qDebug()<<"> onDownloadFinished() download exist error!";
        return;
    }else if(m_dlExistError){
        count = 1;
        QStringList list = {"showErrorCode","1001"};
        emit sigSocketSendMsg(DaemonProxy::ProcessObj::PO_Upgrade,list.join("@&@"));
        return;
    }
    count = 1;
    
    qDebug()<<"> "<<__func__<<"Download all completed";
    QStringList msglist = {"upgradeProgress","50"};
    emit sigSocketSendMsg(DaemonProxy::ProcessObj::PO_Upgrade,msglist.join("@&@"));
    
    QFileInfo fileInfo("/opt/ota/down/copy/");
    QDir dir;
    if(!fileInfo.isDir())
        dir.mkdir("/opt/ota/down/copy/");
    system("cp -r /opt/ota/down/*.zip /opt/ota/down/copy/");
    
    //MD5 校验
    if(!md5Checkout(m_fileSavePath)){
        //TODO send error code 
        qDebug()<<"> onDownloadFinished() md5 check fail!";
        return;
    }
    
    if(m_daemonProxy->userCheck()){
        QtConcurrent::run(this,&OSDeamonService::installUpdatePackage,m_fileSavePath);
    }else if(!m_upgradeSilent && !m_configManage->autoUpgrade()){
        if(m_configManage->tipTimeStatus() == 2){
            // 超过一周，继续提醒
            QDateTime time = QDateTime::currentDateTime();
            int days = time.daysTo(m_configManage->tipTime());
            if(qAbs(days) > 7){
                QtConcurrent::run(this,&OSDeamonService::installUpdatePackage,m_fileSavePath);
            }
        }else{
            QtConcurrent::run(this,&OSDeamonService::installUpdatePackage,m_fileSavePath);
        }
    }else{
        qDebug()<<"> onDownloadFinished() forced upgrade!";
        // 强制升级
        m_configManage->wirteUpdateLoadingFlag(ConfigManage::UpdateFlag::UF_StartUpdate);
        m_configManage->writeUpdatePackageDate(m_tasksQueue);
    }
    return;
}

void OSDeamonService::installUpdatePackage(QList<PckPathCheckCode> packagePath)
{
    m_configManage->wirteUpdateLoadingFlag(ConfigManage::UpdateFlag::UF_StartUpdate);
    m_configManage->writeUpdatePackageDate(m_tasksQueue);
    m_configManage->writeUpdatePackageDate_null();
    QList<QString> pathList;
    for(auto& v: packagePath)
        pathList.append(v.pckPath);
    
    if(!unzipPackage(pathList)){
        qDebug()<<"> unzip fail!";
        m_configManage->wirteUpdateLoadingFlag(ConfigManage::UpdateFlag::UF_UpdateComplete);
        m_configManage->delUpatePackageDate();
        delDownloadFile();
        return;
    }
    if(!runUpdateShell(pathList)){
        qDebug()<<"> install fail!";
        m_configManage->wirteUpdateLoadingFlag(ConfigManage::UpdateFlag::UF_UpdateComplete);
        m_configManage->delUpatePackageDate();
        delDownloadFile();
        return;
    }
    
    qDebug()<<"> PO_Upgrade  funciton "<<__func__<<__LINE__;
    QStringList msgList = {"upgradeProgress","100"};
    emit sigSocketSendMsg(DaemonProxy::ProcessObj::PO_Upgrade,msgList.join("@&@"));
    
    m_configManage->wirteUpdateLoadingFlag(ConfigManage::UpdateFlag::UF_UpdateComplete);
    m_configManage->delUpatePackageDate();
    //TODO version model
    QString version = m_tasksQueue.last().value("version").toString();
    m_configManage->writeNewVersion(version,"default");
    
    qDebug()<<"> PO_Upgrade  funciton "<<__func__<<__LINE__;
    emit sigSocketSendMsg(DaemonProxy::ProcessObj::PO_Upgrade,"showUpgradeDone");
    
    delDownloadFile();
    m_tasksQueue.clear();
    m_fileSavePath.clear();
    emit sigGetNewVersion();
    m_updateFail = true;
    
    qDebug() << __FUNCTION__ << "done ---";
    return;
}

void OSDeamonService::httpReply(const QString &http, QString result, int code)
{
    if(http == m_configManage->urlBindAgent()){
        if(code != 200){
            qDebug()<<"> bind agent http reply error code:"<<code;
        }else{
            QJsonDocument doc = QJsonDocument::fromJson(result.toLatin1());
            QJsonObject obj = doc.object();
            qDebug()<<"> httpReply() QJsonObject:"<<obj;
            m_isBindAgent = obj.value("is_bind_business").toBool();
        }
        m_configManage->writeEsportBoxBindAgent(m_isBindAgent,m_registerOk);
    }
    if(http == m_configManage->urlMacIP()){
        qDebug()<<"> httpReply() mac/ip reply code:"<<code<<" result:"<<result;
        if(!m_timerHeart){
            m_timerHeart = new QTimer();
            connect(m_timerHeart,&QTimer::timeout,this,[&](){
                QJsonObject jsobj;
                jsobj.insert("heartbeat","");
                if(m_mqttDaemon)
                    m_mqttDaemon->publishTheme(MqttDaemon::Topic::Property_pub_heartRequest,jsobj);
            });
            m_timerHeart->start((m_configManage->timeout()/10)*1000);
        }
    }
}

bool OSDeamonService::unzipPackage(QList<QString> packagePath)
{
    if(packagePath.size() <= 0){
        //TODO
        return false;        
    }
    //检查包的完整性
    for(auto & unzipPath:packagePath){
        if(!QFile::exists(unzipPath)){
            //TODO
            QStringList list = {"showErrorCode","1003"};
            emit sigSocketSendMsg(DaemonProxy::ProcessObj::PO_Upgrade,list.join("@&@"));
            return false;
        }
    }
    //解压
    QEventLoop loop;
    int size = packagePath.size();
    bool unzipOK = true;
    for(auto& unzipPath : packagePath){
        QThreadPool::globalInstance()->start([&](){
            qDebug() << __FUNCTION__ << ", filename:" << unzipPath;
            QString cmd = QString("unzip -o %1 -d %2")
                              .arg(unzipPath,QString(PRODUCT_UNZIP_PATH));
            qDebug()<<"> unzip path:"<<cmd;
            QProcess unzip;
            connect(&unzip,QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished),this,[&](int exitCode, QProcess::ExitStatus exitStatus){
                qDebug() << "unPackage finish, exitCode:" << exitCode << ", exitStatus:" << exitStatus;
                --size;
                if(exitCode != 0){
                    unzipOK = false;
                }
                if(size <= 0)
                    loop.quit();
                
            });
            unzip.start("bash",QStringList()<<"-c"<<cmd);
            unzip.waitForFinished(-1);
        });
    }
    loop.exec();
    return unzipOK;
}

bool OSDeamonService::runUpdateShell(QList<QString> packagePath)
{
    if(packagePath.size() <= 0){
        //TODO
        return false;        
    }
    //检查更新shell脚本
    for(auto& v : packagePath){
        qDebug()<<"> file path:"<<v;
        QString path = v;
        QString name = path.remove(QString(".%1").arg(QFileInfo(path).suffix()));
        QString updateShellFile = QString("%1/update.sh").arg(name);
        if(!QFile::exists(updateShellFile)){
            qDebug()<<"> update shell file not exist!";
            return false;
        }
        if(!QFileInfo(updateShellFile).isExecutable()){
            QProcess pro;
            QString cmd = QString("chmod +x %1").arg(updateShellFile);
            pro.start("bash",QStringList()<<"-c"<<cmd);
            pro.waitForFinished();
        }
    }
    
    //安装，运行shell脚本
    bool installOk = true;
    for(int i = 0; i < packagePath.size();i++){
        
        qDebug()<<"> file path:"<<packagePath[i];
        QString path = packagePath[i];
        QString name = path.remove(QString(".%1").arg(QFileInfo(path).suffix()));
        QString cmd = QString("%1/update.sh").arg(name);
        qDebug() << __FUNCTION__ << "cmd:" << cmd;
        
        QProcess pro;
        connect(&pro,&QProcess::readyReadStandardOutput,this,[&](){
            QString rate = pro.readAllStandardOutput();
            qDebug() << "readyReadStandardOutput ---- " << rate.toInt();
            int r = rate.toInt();
            if(r == 400){
                return;
            }
            if(r == 1004){
                QStringList list = {"showErrorCode","1004"};
                qDebug()<<"> PO_Upgrade  funciton "<<__func__<<__LINE__;
                emit sigSocketSendMsg(DaemonProxy::ProcessObj::PO_Upgrade,list.join("@&@"));
                pro.finished(1,QProcess::ExitStatus::CrashExit);
                return;
            }
            if(r < 0)
                return;
            if(r == 200){
                //emit sigSocketSendMsg(DaemonProxy::ProcessObj::PO_Upgrade,"showContinueButton");
                r = 100;
            }
            if(r > 100)
                r = 100;
            r = qMin(r,100);
            qreal pi_a = (i == 0) ? 0 : (static_cast<qreal>(i)/static_cast<qreal>(packagePath.size())*50.0);
            qreal pi_b = (rate.toDouble()/100.0)*(50.0/static_cast<qreal>(packagePath.size()));
            qreal pi = qreal(pi_a+pi_b); 
            //qreal pi = ((i/packagePath.size())*50)+((rate.toDouble()/100.0)*(50.0/packagePath.size()));
            //qreal pi = qreal((((i*1.0)/(packagePath.size()*1.0))*50.0)+((rate.toDouble()/100.0)*(50.0/(packagePath.size()*1.0))));
            qDebug()<<"> pi:"<<(50+pi)<<" recv rate:"<<r<<" pi first value:";
            QStringList msglist = {"upgradeProgress",QString::number(pi + 50)};
            qDebug()<<"> PO_Upgrade  funciton "<<__func__<<__LINE__;
            if((pi+50) <= 100)
                emit sigSocketSendMsg(DaemonProxy::ProcessObj::PO_Upgrade,msglist.join("@&@"));
        });
        
        connect(&pro, &QProcess::readyReadStandardError, this, [&](){
            QString rate = pro.readAllStandardError();
            qDebug() << "readyReadStandardError ---- " << rate;
        });
        
        connect(&pro,QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished),this,[&](int exitCode, QProcess::ExitStatus exitStatus){
            qDebug() << "installPackage finish, exitCode:" << exitCode << ", exitStatus:" << exitStatus;
            if(exitCode != 0){
                qDebug()<<"shell run error,error code:"<<exitCode;
                installOk = false;
            }
        });
        pro.start("bash",QStringList()<<"-c"<<cmd);
        pro.waitForFinished(-1);
    }
    qDebug()<<"> runUpdateShell() end";
    return installOk;
}

bool OSDeamonService::isUpdatePckExists(const QList<QJsonObject> & tasks, QList<PckPathCheckCode> & pathList)
{
    if(tasks.size() <= 0){
        return false;
    }
    pathList.clear();
    for(auto& v : tasks){
        OSDeamonService::PckPathCheckCode pcc;
        QString url = v.value("file_url").toString();
        QString md5Code = v.value("md5").toString();
        QString path = QString(PRODUCT_UNZIP_PATH);
        
        url.replace("\\","/");
        QString savePath = QUrl::fromUserInput(url).fileName();
        savePath.prepend(path+'/');
        QFileInfo fileInfo(savePath);
        if(!QFile::exists(savePath) && fileInfo.size() == 0){
            pathList.clear();
            return false;
        }
        pcc.pckPath = savePath;
        pcc.md5Code = md5Code;
        pathList.append(pcc);
    }
    return true;
}

void OSDeamonService::delDownloadFile()
{
    qDebug()<<"> delDownloadFile() delete download all files!";

    QProcess pro;
    //command (rm -rf /opt/ota/down/*)
    QString cmd = QString("rm -rf %1*").arg(PRODUCT_UNZIP_PATH);
    qDebug()<<"> delete folder all files command:"<<cmd;
    pro.start("bash",QStringList()<<"-c"<<cmd);
    pro.waitForFinished();
}

void OSDeamonService::sendMacIP()
{
    int ip_interface_flag = -1;
    QString computer_ip ,computer_mac;
    QString cmd_ip = "ip a | grep -A 2 enp2s0 | grep \"global\" | awk '{print $2}'";
    qDebug()<<"> sendMacIP() cmd ip:"<<cmd_ip;
    QProcess process;
    process.start("bash",QStringList()<<"-c"<<cmd_ip);
    process.waitForFinished(-1);
    QTextStream txtStream(&process);
    QString cmd_ip_result(txtStream.readAll());
    cmd_ip_result.remove("\n");
    
    if(cmd_ip_result.isEmpty()){
        cmd_ip.clear();
        cmd_ip = "ip a | grep -A 2 wlp1s0 | grep \"global\" | awk '{print $2}'";
        qDebug()<<"> sendMacIP() cmd ip:"<<cmd_ip;
        process.start("bash",QStringList()<<"-c"<<cmd_ip);
        process.waitForFinished(-1);
        cmd_ip_result.clear();
        cmd_ip_result = process.readAllStandardOutput();
        
        if(!cmd_ip_result.isEmpty()){
            ip_interface_flag = 0;
            computer_ip = cmd_ip_result.split("/").first();
        }
    }else{
        ip_interface_flag = 1;
        computer_ip = cmd_ip_result.split("/").first();
        
    }
    
    QString cmd_mac = ip_interface_flag == -1?QString():
                          (ip_interface_flag == 0)?"ip link | grep -A 1 wlp1s0 | grep ether | awk '{print $2}'":
                                                   "ip link | grep -A 1 enp2s0 | grep ether | awk '{print $2}'";
    qDebug()<<"> sendMacIP() cmd mac:"<<cmd_mac;
    QJsonObject jsobj;
    if(ip_interface_flag == -1){
        jsobj.insert("ip","");
        jsobj.insert("mac","");
    }else{
        process.start("bash",QStringList()<<"-c"<<cmd_mac);
        process.waitForFinished(-1);
        
        computer_mac = process.readAllStandardOutput();
        computer_mac.remove("\n");
        
        jsobj.insert("ip",computer_ip);
        jsobj.insert("mac",computer_mac);
    }
    jsobj.insert("device_sn",m_configManage->deviceId());
    jsobj.insert("product_model_id",m_configManage->productKey());
    m_downloadTasks->postHttp(m_configManage->urlMacIP(),jsobj);
    
    qDebug()<<"> sendMacIP() json date:"<<jsobj;
}

bool OSDeamonService::checkNetwork()
{
    return EBNetwork::Get()->checkNet();
}

//TODO not md5 code
bool OSDeamonService::md5Checkout(QList<PckPathCheckCode> filePath/*,QString md5*/)
{
    if(filePath.size()<=0){
        qDebug()<<"> checkout package list is null!";
        return false;
    }
   
    for(auto& date : filePath){
        QFile file(date.pckPath);
        if(!file.open(QFile::ReadOnly)){
            qDebug() << "Could not open " + date.pckPath + " for ReadOnly: " + file.errorString();
            return false;
        }
        QCryptographicHash hash(QCryptographicHash::Md5);
        hash.addData(&file);
        file.close();
        qDebug() << "file hash:"  << hash.result().toHex();
        qDebug() << "cloud hash:"  << date.md5Code;
        if(hash.result().toHex() != date.md5Code){
            delDownloadFile();
            qDebug() << "file md5 diff, no remove " << date.pckPath;
            QStringList list = {"showErrorCode","1002"};
            emit sigSocketSendMsg(DaemonProxy::ProcessObj::PO_Upgrade,list.join("@&@"));
            return false;
        }
    }
    return true;
}

void OSDeamonService::onFinished()
{
    if(!m_thd)
        return;
    m_thd->quit();
    m_thd->wait();
    
    return;
}

void OSDeamonService::onReadTaskFile()
{
    qDebug()<<"> "<<__func__;

    //TODO 重新写逻辑 将下载完成的函数，逻辑处理部分分离出来
    if(m_configManage->isExistBreakOff()){
        m_configManage->readBreakOffDate(m_tasksQueue);
        m_fileSavePath.clear();
        QFileInfo fileInfo("/opt/ota/down/copy/");
        if(fileInfo.isDir()){
            system("cp -r /opt/ota/down/copy/*.zip /opt/ota/down/");
        }
        if(isUpdatePckExists(m_tasksQueue,m_fileSavePath) && md5Checkout(m_fileSavePath)){
            qDebug()<<"> onReadTaskFile() upgrade update!";
            emit sigSocketSendMsg(DaemonProxy::ProcessObj::PO_Upgrade,"showUpgradeProgress");
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)        
            QtConcurrent::run(this,&OSDeamonService::installUpdatePackage,m_fileSavePath);
#else
            QtConcurrent::run(&OSDeamonService::installUpdatePackage,this,m_fileSavePath);
#endif
            return;
        }
    }
    onPostOtaFirmwareGet();
}

//TODO
void OSDeamonService::onDoUpgrade()
{
    qDebug()<<"> "<<__func__;
    emit sigSocketSendMsg(DaemonProxy::ProcessObj::PO_Upgrade,"showUpgradeProgress");
    if(m_downloadTasks->status() == DownloadTasks::DownloadStatus::DS_Finished){
        m_queueSize = m_tasksQueue.size();
        emit sigStartDownloadTasks();
    }
}

void OSDeamonService::onShutdown()
{
    qDebug()<<"> "<<__func__;
    
    //QString cmd = "shutdown -H now";
    QString cmd = "systemctl poweroff -i";
    qDebug() << __FUNCTION__ << "cmd:" << cmd;
    QProcess pro;
    pro.startDetached("bash", QStringList() << "-c" << cmd);
    //pro.waitForFinished();
}

void OSDeamonService::onReboot()
{
    qDebug()<<"> "<<__func__;
    
    //QString cmd = "shutdown -r now";
    QString cmd = "systemctl reboot -i";
    qDebug() << __FUNCTION__ << "cmd:" << cmd;
    QProcess pro;
    pro.startDetached("bash", QStringList() << "-c" << cmd);
    //pro.waitForFinished(-1);
}

void OSDeamonService::onSDDMRegister()
{
    qDebug()<<"> "<<__func__;
    if(m_registerOk){
        m_configManage->writeSDDMRegister(m_registerOk);
        emit sigSocketSendMsg(DaemonProxy::ProcessObj::PO_SDDM,m_registerOk?"success":"failed");
    }else
        m_mqttDevReg->publishTheme(MqttDeviceReg::Topic::Device_register,QJsonObject());
}

void OSDeamonService::onPower()
{
    qDebug()<<"> onPower()";
    
    if(m_daemonProxy->errorReShut() || (m_updateFail && m_updataVersionDate)){
        m_daemonProxy->reboot()?onReboot():m_daemonProxy->shutdown()?onShutdown():void();
    }
}

bool OSDeamonService::dlExistError() const
{
    return m_dlExistError;
}

void OSDeamonService::setDlExistError(bool newDlExistError)
{
    m_dlExistError = newDlExistError;
}

quint64 OSDeamonService::configTimerout()
{
    return m_configManage->timeout();
}

void OSDeamonService::initMqttDaemon(QMap<QString, QString> mqttInfoMap)
{
    if(m_mqttDaemon || mqttInfoMap.isEmpty()){
        qDebug()<<"> initMqttDaemon() mqtt daemon already initialized";
        return;
    }
    //create MqttDaemon object
    m_mqttDaemon = new MqttDaemon(mqttInfoMap,this);
    connect(m_mqttDaemon,&MqttDaemon::connService,this,&OSDeamonService::onRecvDeamonConn,Qt::QueuedConnection);
    connect(m_mqttDaemon,&MqttDaemon::diconn,this,[&](){
            m_connectMqttDaemon = false;
            m_isPckDateOK = false;
        },Qt::QueuedConnection);
    connect(m_mqttDaemon,&MqttDaemon::propertyReply,m_configManage,&ConfigManage::onWriteQRCodeUrl,Qt::QueuedConnection);
    m_mqttDaemon->mqttMonitor();
    qDebug()<<"> mqtt daemon connect data:"<<mqttInfoMap;
}
namespace Auxi {
bool increSort(const QString &a1,const QString &a2){
    QStringList a1List = a1.split(".");
    QStringList a2List = a2.split(".");
    int listSize = qMin(a1List.size(), a2List.size());
    
    for (int i = 0; i < listSize; ++i) {
        int v1 = a1List[i].toInt();
        int v2 = a2List[i].toInt();
        if (v1 != v2)
            return v1 < v2; 
    }
    return a1List.size() < a2List.size();
}
}

